<?php
/**
 * The PantsDB class maintains a database connection and provides methods for
 * working with the database.
 *
 * @author Ryan Grove <ryan@wonko.com>
 * @copyright Copyright (c) 2007 Ryan Grove. All rights reserved.
 * @license http://opensource.org/licenses/bsd-license.php New BSD License
 */
class PantsDB {
  private $db;
  private $queries = array();
  
  // -- Public Instance Methods ------------------------------------------------
  
  /**
   * Opens a database connection and initializes a PantsDB object.
   *
   * @param string $host database server hostname
   * @param string $user database username
   * @param string $pass database password
   * @param string $name database name
   */
  public function __construct($host, $user, $pass, $name) {
    // Open database connection.
    $this->db = new mysqli($host, $user, $pass, $name);
    
    if (mysqli_connect_errno()) {
      if (DEBUG) {
        Pants::error('Database Connection Error',
            mysqli_connect_error());
      }
      else {
        Pants::error('Database Connection Error',
            'Unable to connect to the database.');
      }
    }

    // Load predefined queries.
    foreach(simplexml_load_file(PANTS_DIR.'/db/queries.xml') as $query) {
      $this->queries[(string) $query['name']] = array(
        'class' => isset($query['class']) ? (string) $query['class'] : null,
        'many'  => isset($query['many']) ? true : false,
        'sql'   => (string) $query,
      );
    }
  }

  /**
   * Gets the most recent insert id returned as a result of creating a new 
   * database row.
   *
   * @return int most recent insert id
   */
  public function getInsertId() {
    return $this->db->insert_id;
  }

  /**
   * Executes a named database query using the specified parameters and returns
   * the result.
   *
   * @param string $name query name
   * @param array $parameters query parameters
   * @return mixed query result 
   */
  public function query($name, $parameters = array()) {
    if (!isset($this->queries[$name])) {
      throw new PantsException("Undefined query: $name");
    }

    $query = $this->queries[$name];
    $sql   = $query['sql'];

    if (count($parameters)) {
      $formattedParams = array();

      // Prepend a ':' to each parameter name and escape the value properly.
      foreach($parameters as $paramName => $paramValue) {
        if (!is_numeric($paramValue)) {
          $paramValue = is_null($paramValue) ? 'NULL' : "'".
              $this->db->real_escape_string($paramValue)."'";
        }

        $formattedParams[":$paramName"] = $paramValue;
      }

      // Replace parameter names in the query with assigned values.
      $sql = strtr($sql, $formattedParams);
    }

    // Display the query if SHOWSQL is true.
    if (SHOWSQL) {
      echo htmlentities($sql)."<br />\n";
    }

    // Execute the query and return the result(s).
    return $this->fetchResults($query, $this->db->query($sql));
  }
  
  // -- Private Instance Methods -----------------------------------------------
  
  /**
   * Gets results from the specified query result object in a format
   * appropriate for the specified query.
   *
   * @param array $query query
   * @param mysqli_result $result query result
   * @return mixed query results
   */
  private function fetchResults($query, $result) {
    if (!$result) {
      throw new PantsException('Database Error');
    }
    
    // Return results as objects of a particular class.
    if ($query['class']) {

      // Return an array of objects.
      if ($query['many']) {
        $objects = array();

        while($row = $result->fetch_array()) {
          $objects[] = call_user_func(array($query['class'], 'load'), $row);
        }
        
        return $objects;
      }

      // Return a single object.
      if ($result->num_rows > 0) {
        return call_user_func(array($query['class'], 'load'), $result->fetch_array());
      }
      
      // No results returned.
      return null;
    }

    // Return a raw mysqli result object if no class was specified in the query.
    return $result;
  }
}
?>